package com.ruoyi.framework.aspectj;

import cn.hutool.json.JSONUtil;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

/**
 * @Desc: 接口响应aop
 * @Author LuoYong
 * @Date 2022/1/13 15:00
 */
@Slf4j
@AllArgsConstructor
@Aspect
@Component
public class ResponseAspect {

    private final Validator validator;
    private static final String REQUEST_ID = "requestId";

    /**
     * 拦截controller所有方法 环绕通知
     *
     * @param proceeding
     * @return
     */
    @Around("execution (* com.ruoyi.project.lely.controller..*.*(..))")
    @SneakyThrows
    public Object around(ProceedingJoinPoint proceeding) {
        MethodSignature methodSignature = (MethodSignature) proceeding.getSignature();
        Method method = methodSignature.getMethod();
        // JmsListener jmsListener = method.getAnnotation(JmsListener.class);
        // if (jmsListener != null) {
        //     return proceeding.proceed();
        // }
        String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();
        // 数据校验
        Validated validated = method.getAnnotation(Validated.class);
        // 清空MDC
        MDC.clear();
        // 增加requestId用于追踪打印日志中的请求
        MDC.put(REQUEST_ID, UUID.randomUUID().toString());
        Object[] objects = proceeding.getArgs();
        List<Object> list = new ArrayList<>();
        for (Object obj : objects) {
            // 接口参数禁止使用HttpServletRequest和HttpServletResponse
            if (obj == null || obj instanceof HttpServletRequest || obj instanceof HttpServletResponse
                    || obj instanceof MultipartFile) {
                continue;
            }
            list.add(obj);
            Set<ConstraintViolation<Object>> set;
            if (validated == null) {
                set = validator.validate(obj);
            } else {
                set = validator.validate(obj, validated.value());
            }
            if (!set.isEmpty()) {
                throw new ConstraintViolationException(set);
            }
        }
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
            return proceeding.proceed();
        } finally {
            stopWatch.stop();
            log.info("接口方法:{},参数:{},耗时:{}ms", methodName, JSONUtil.toJsonStr(list), stopWatch.getTotalTimeMillis());
        }
    }
}
